DETECCIÓN DE OBJETOS DE CLASES NO PRE-ENTRENADAS¶

Para la mejora del proceso de detección de eventos de cumpleaños se entrenará una red YOLO sobre imagenes de velas de cumpleaños. Este tipo de objetos no ha sido preentrenado en los modelos YOLO v8 proporcionados por Ultralytics.

0. Importación packages y constantes¶

In [1]:
import pandas as pd
import numpy as np
import os
import networkx as nx
import matplotlib.pyplot as plt
from PIL import Image
from ultralytics import YOLO


# Packages propios
from utils.objectdetection import ObjectDetectionHelper
from utils.graphdbmanipulation import ImagesGraphDB
from utils.imagesmanipulation import ImageHelper
from utils.analytics import AnalyticsHelper
In [2]:
# Rutas de interés
PATH_IMAGENES_A_ANALIZAR = "./resources/images/Movil-S21"
PATH_MODELO_YOLO_COCO = "./models/yolov8x.pt"
PATH_COCO_LABELS_REF_FILE = "./resources/reference_labels/coco_labels.csv"
PATH_COCO_GML_FILE = './outputs/graph_databases/graph_OBJECTS.gml'
PATH_COCO_DETECTED_LABELS = './outputs/detected_objects/Movil-S21_labels_predicted_COCO/labels'
# Modelo para la detección de llamas de velas
PATH_MODELO_YOLO_CANDLE_LIGHT = "./models/yolov8x_candle_light.pt"
PATH_CANDLE_LIGHT_DETECTED_LABELS= './outputs/detected_objects/Movil-S21_labels_predicted_CANDLE_LIGHT/labels'
PATH_CANDLE_LIGHT_LABELS_REF_FILE =  "./resources/reference_labels/candle_light_labels.csv"
# Modelo para la detección de rios y lagos
PATH_MODELO_YOLO_RIVERS_LAKES = "./models/yolov8x_rivers_lakes.pt"
PATH_RIVERS_LAKES_DETECTED_LABELS = './outputs/detected_objects/Movil-S21_labels_predicted_RIVERS_LAKES/labels'
PATH_RIVERS_LAKES_LABELS_REF_FILE =  "./resources/reference_labels/rivers_lakes_labels.csv"
# Modelo para la detección de rios y lagos alternativo
PATH_MODELO_YOLO_RIVERS_LAKES2 = "./models/yolov8x_rivers_lakes2.pt"
PATH_RIVERS_LAKES2_DETECTED_LABELS = './outputs/detected_objects/Movil-S21_labels_predicted_RIVERS_LAKES2/labels'
PATH_RIVERS_LAKES2_LABELS_REF_FILE =  "./resources/reference_labels/rivers_lakes_labels2.csv"
# GRAFO DE DETECCIÓN DE OBJETOS COCO y PERSONALIZADOS
PATH_COCO_AND_CUSTOM_OBJECTS_GML_FILE = './outputs/graph_databases/graph_OBJECTS_CANDLES.gml'
PATH_RIVERS_LAKES_GML_FILE = './outputs/graph_databases/graph_RIVERS_LAKES.gml'

1. Entrenamiento de red YOLO para la detección de "llamas de velas"¶

Se entrena la red YOLO para detectar llamas de velas sobre un conjunto de 100 imágenes.

In [ ]:
# Load a pretrained YOLO model (recommended for training)
model = YOLO(model= PATH_MODELO_YOLO_COCO)

# Train the model using the 'coco128.yaml' dataset for 3 epochs
results = model.train(data='./resources/YOLO_yaml/yolo_v8_candle_light.yaml', epochs=10, batch=10,name='./models/yolov8x_candle_light')

# Evaluate the model's performance on the validation set
results = model.val()
In [5]:
# Se muestran los resultados del entrenamiento y validación
%matplotlib inline
fig, ax = plt.subplots(figsize=(20, 20))
ax.axis('off')
img = np.asarray(Image.open('./outputs/models_train_val_results/yolov8x_candle_light/results.png'))
ax.imshow(img)
Out[5]:
<matplotlib.image.AxesImage at 0x1f2b2095ed0>

2. Detección de "velas" en imágenes¶

A partir del nuevo modelo obtenido especializado en encontrar "llamas de velas" en imagenes se analizan todas las imagenes de estudio de cara poder detectar velas de cumpleaños y poder "cribar" de un mejor modo eventos de cumpleaños.

In [ ]:
objectdetector = ObjectDetectionHelper()
objectdetector.detectionTrainedClasses('./models/yolov8x_candle_light.pt',PATH_IMAGENES_A_ANALIZAR,0.2)

3. Análisis de resultados de la mejora de detección de cumpleaños añadiendo detección de "velas"¶

3.1. Carga de etiquetas manuales¶

In [3]:
df_images_labeled_manually = pd.read_excel('./resources/manual_labels/etiquetado_manual_imagenes.xlsx',skiprows=1)
df_images_labeled_manually.head(10)
Out[3]:
Image Viaje Comida Cena Cumpleaños Retrato Maria Biel Yo Piscina ... Tenedor Cuchillo Cuchara bowl pastel silla Mesa Plato Vaso Llama vela
0 20210724_100218.jpg 0 0 0 0 0 0 1 0 0 ... 0 0 0 0 0 1 1 1 0 0
1 20210724_104730.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
2 20210724_104736.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
3 20210724_104739.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
4 20210724_104743.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
5 20210724_104757.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
6 20210724_200143.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 1 0 0 0
7 20210724_200145.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 1 0 0 0
8 20210724_200231_02.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 1 0 0 0
9 20210724_200231_03.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 0 0 0 0

10 rows × 27 columns

3.2. Evaluación del modelo¶

3.2.1. Imágenes con objetos detectados (persona, pastel) - opción 1¶

In [4]:
# Se cargan los datos desde un fichero gml de los objetos detectados
imagesGraphDB = ImagesGraphDB()
imagesGraphDB.load_graph_from_gml_file(PATH_COCO_GML_FILE)
In [5]:
# Se cargan el grafo con los objetos detectados en cada imagen
imagesGraphDB.load_graph_from_yolo_detected_objects_txt_files(PATH_CANDLE_LIGHT_LABELS_REF_FILE,PATH_CANDLE_LIGHT_DETECTED_LABELS)
# Se guarda el grafo en la carpeta de "outputs" en fichero gml
imagesGraphDB.write_gml_file(PATH_COCO_AND_CUSTOM_OBJECTS_GML_FILE)
In [5]:
# Se obtiene las imágenes del grafo que se relacionan con los nodos "person" y "cake"
object_classes_to_filter = ['person','cake'] # Se cogerán las imágenes con pasteles, personas y velas encendidas
neighbors_cake = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(object_classes_to_filter,[0.7,0.6])
cake_images_filepath_list = []
cake_images_filename_list = []
for neighbor_cake in neighbors_cake:
    cake_images_filepath_list.append(PATH_IMAGENES_A_ANALIZAR+'_resized/'+neighbor_cake)
    cake_images_filename_list.append(neighbor_cake)

# Se cargan las imágenes en formato PIL en una lista pora poderlas mostrar en un grid
images_rgb_list= []
for i, image in enumerate(cake_images_filepath_list):
    image_rgb = Image.open(image)
    images_rgb_list.append(image_rgb)

# Se muestran las imagenes en un grid usando un metodo propio del helper de imágenes
%matplotlib inline
ImageHelper.display_images(images_rgb_list,columns=3,max_images=20,label_font_size=12)

3.2.2. Matriz de confusión - opción 1¶

In [6]:
# Primero se obtienen los valores de "Cumpleaños" etiquetados manualmente en todas las imágenes.
df_images_birthday_manual = df_images_labeled_manually[['Image','Cumpleaños']].copy()
# Se obtienen las imágenes del grafo que contienen los tres tipos de objetos
neighbors_birthday = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(['person','cake'],[0.7,0.6])
# Se obtiene el dataframe de imágenes detectadas mediante el primer modelo
df_images_birthday_detected = pd.DataFrame(neighbors_birthday, columns=['Image'])
df_images_birthday_detected['Cumpleaños predicted'] =1
# Se cruzan ambos dataframes
df_images_birthday_analysis = df_images_birthday_manual.merge(df_images_birthday_detected , how="left", left_on="Image", right_on="Image")
# Se informa con valor 0 aquellas imagenes sin detección de rios y lagos
# en lugar de NA
df_images_birthday_analysis['Cumpleaños predicted'] = df_images_birthday_analysis['Cumpleaños predicted']. fillna(0)

# Se convierte a int la columna ya que se usa como booleano
df_images_birthday_analysis['Cumpleaños predicted'] = df_images_birthday_analysis['Cumpleaños predicted'].astype('int')

df_images_birthday_analysis
Out[6]:
Image Cumpleaños Cumpleaños predicted
0 20210724_100218.jpg 0 0
1 20210724_104730.jpg 0 0
2 20210724_104736.jpg 0 0
3 20210724_104739.jpg 0 0
4 20210724_104743.jpg 0 0
... ... ... ...
1360 IMG_20180505_212946_BURST001_COVER.jpg 0 0
1361 IMG_20180505_212946_BURST002.jpg 0 0
1362 IMG_20180505_212946_BURST003.jpg 0 0
1363 IMG_20180822_173932.jpg 0 0
1364 IMG_20180824_195613.jpg 0 0

1365 rows × 3 columns

In [7]:
from sklearn import metrics

# Se cargan los resultados de la predicción y el real
images_birthday_actual= df_images_birthday_analysis['Cumpleaños'].to_numpy()
images_birthday_predicted= df_images_birthday_analysis['Cumpleaños predicted'].to_numpy()
# Se cargan los datos para generar la matriz de confusión
confusion_matrix = metrics.confusion_matrix(images_birthday_actual, images_birthday_predicted)
cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = confusion_matrix, display_labels = [False, True])
# Se muestra el gráfico
cm_display.plot()
plt.show()

3.2.3. Métricas -opción 1¶

In [8]:
analyticsHelper = AnalyticsHelper()
metrics = analyticsHelper.getBinaryClassificationConfusionMatrixMetrics(confusion_matrix)
metrics
Out[8]:
{'recall': 0.2777777777777778,
 'precision': 0.3333333333333333,
 'accuracy': 0.9831501831501831,
 'specificity': 0.9925760950259837,
 'f1_score': 0.303030303030303}

3.2.1. Imágenes con objetos detectados (persona, pastel, llama de vela) - opción 2¶

In [8]:
# Se obtiene las imágenes del grafo que se relacionan con los nodos "person", "cake" y "candle_light"
object_classes_to_filter = ['person','cake','candle_light'] # Se cogerán las imágenes con pasteles, personas y velas encendidas
neighbors_cake = imagesGraphDB.get_images_containing_list_object_types(object_classes_to_filter)
cake_images_filepath_list = []
cake_images_filename_list = []
for neighbor_cake in neighbors_cake:
    cake_images_filepath_list.append(os.path.join(PATH_IMAGENES_A_ANALIZAR+'_resized',neighbor_cake))
    cake_images_filename_list.append(neighbor_cake)

# Se cargan las imágenes en formato PIL en una lista pora poderlas mostrar en un grid
images_rgb_list= []
for i, image in enumerate(cake_images_filepath_list):
    image_rgb = Image.open(image)
    images_rgb_list.append(image_rgb)

# Se muestran las imagenes en un grid usando un metodo propio del helper de imágenes
%matplotlib inline
ImageHelper.display_images(images_rgb_list,columns=3,max_images=20,label_font_size=12)

Se obtienen las imágenes de cumpleaños, pero aparecen también una comida debido a que se confunde un trozo de carne con un pastel y también hay un pastel que no tiene velas y es por otra celebración. Con la combinación de estos tres items 'person','cake' y 'candle_light'. En general los resultados son buenos al detectarse más de una foto de todos los cumpleaños celebrados sobre un conjunto de más de mil fotos.

3.2.2. Matriz de confusión - opción 2¶

In [9]:
# Primero se obtienen los valores de "Cumpleaños" etiquetados manualmente en todas las imágenes.
df_images_birthday_manual = df_images_labeled_manually[['Image','Cumpleaños']].copy()
# Se obtienen las imágenes del grafo que contienen los tres tipos de objetos
neighbors_birthday = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(['person','cake','candle_light'],[0.7,0.6,0.2])
# Se obtiene el dataframe de imágenes detectadas mediante el primer modelo
df_images_birthday_detected = pd.DataFrame(neighbors_birthday, columns=['Image'])
df_images_birthday_detected['Cumpleaños predicted'] =1
# Se cruzan ambos dataframes
df_images_birthday_analysis = df_images_birthday_manual.merge(df_images_birthday_detected , how="left", left_on="Image", right_on="Image")
# Se informa con valor 0 aquellas imagenes sin detección de rios y lagos
# en lugar de NA
df_images_birthday_analysis['Cumpleaños predicted'] = df_images_birthday_analysis['Cumpleaños predicted']. fillna(0)

# Se convierte a int la columna ya que se usa como booleano
df_images_birthday_analysis['Cumpleaños predicted'] = df_images_birthday_analysis['Cumpleaños predicted'].astype('int')

df_images_birthday_analysis
Out[9]:
Image Cumpleaños Cumpleaños predicted
0 20210724_100218.jpg 0 0
1 20210724_104730.jpg 0 0
2 20210724_104736.jpg 0 0
3 20210724_104739.jpg 0 0
4 20210724_104743.jpg 0 0
... ... ... ...
1360 IMG_20180505_212946_BURST001_COVER.jpg 0 0
1361 IMG_20180505_212946_BURST002.jpg 0 0
1362 IMG_20180505_212946_BURST003.jpg 0 0
1363 IMG_20180822_173932.jpg 0 0
1364 IMG_20180824_195613.jpg 0 0

1365 rows × 3 columns

In [10]:
from sklearn import metrics

# Se cargan los resultados de la predicción y el real
images_birthday_actual= df_images_birthday_analysis['Cumpleaños'].to_numpy()
images_birthday_predicted= df_images_birthday_analysis['Cumpleaños predicted'].to_numpy()
# Se cargan los datos para generar la matriz de confusión
confusion_matrix = metrics.confusion_matrix(images_birthday_actual, images_birthday_predicted)
cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = confusion_matrix, display_labels = [False, True])
# Se muestra el gráfico
cm_display.plot()
plt.show()

3.2.3. Métricas - opción 2¶

In [11]:
analyticsHelper = AnalyticsHelper()
metrics = analyticsHelper.getBinaryClassificationConfusionMatrixMetrics(confusion_matrix)
metrics
Out[11]:
{'recall': 0.2222222222222222,
 'precision': 0.4444444444444444,
 'accuracy': 0.9860805860805861,
 'specificity': 0.9962880475129918,
 'f1_score': 0.2962962962962963}

3.2.4. Conclusiones¶

Con la combinación de la detección de llamas de velas con la detección de personas y tartas se consigue mejorar la precisión de un valor de 0.33 a 0.44, eso implica una reducción del número de falsos de positivos.

4. Entrenamiento de red YOLO para la detección de "rios y lagos"¶

Se entrena un modelo YOLOv8x para detectar "boundary boxes" de agua de rios y lagos en que se ha seleccionado un contorno de toda el río incluyendo su ribera y elementos como personas que puedan estar en su interior.

In [14]:
# Load a pretrained YOLO model (recommended for training)
model = YOLO(model=PATH_MODELO_YOLO_COCO)

# Train the model using the 'coco128.yaml' dataset for 3 epochs
results = model.train(data='./resources/YOLO_yaml/yolo_v8_rivers_lakes.yaml', epochs=10, batch=10,name='./models/yolov8x_rivers_lakes')

# Evaluate the model's performance on the validation set
results = model.val()
New https://pypi.org/project/ultralytics/8.0.90 available  Update with 'pip install -U ultralytics'
Ultralytics YOLOv8.0.53  Python-3.10.11 torch-1.13.1+cpu CPU
yolo\engine\trainer: task=detect, mode=train, model=./models/yolov8x.pt, data=./resources/YOLO_yaml/yolo_v8_water.yaml, epochs=10, patience=50, batch=10, imgsz=640, save=True, save_period=-1, cache=False, device=None, workers=8, project=None, name=./models/yolov8x_water3, exist_ok=False, pretrained=False, optimizer=SGD, verbose=True, seed=0, deterministic=True, single_cls=False, image_weights=False, rect=False, cos_lr=False, close_mosaic=10, resume=False, overlap_mask=True, mask_ratio=4, dropout=0.0, val=True, split=val, save_json=False, save_hybrid=False, conf=None, iou=0.7, max_det=300, half=False, dnn=False, plots=True, source=None, show=False, save_txt=False, save_conf=False, save_crop=False, hide_labels=False, hide_conf=False, vid_stride=1, line_thickness=3, visualize=False, augment=False, agnostic_nms=False, classes=None, retina_masks=False, boxes=True, format=torchscript, keras=False, optimize=False, int8=False, dynamic=False, simplify=False, opset=None, workspace=4, nms=False, lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=7.5, cls=0.5, dfl=1.5, fl_gamma=0.0, label_smoothing=0.0, nbs=64, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.0, copy_paste=0.0, cfg=None, v5loader=False, tracker=botsort.yaml, save_dir=runs\detect\models\yolov8x_water32
Overriding model.yaml nc=80 with nc=1

                   from  n    params  module                                       arguments                     
  0                  -1  1      2320  ultralytics.nn.modules.Conv                  [3, 80, 3, 2]                 
  1                  -1  1    115520  ultralytics.nn.modules.Conv                  [80, 160, 3, 2]               
  2                  -1  3    436800  ultralytics.nn.modules.C2f                   [160, 160, 3, True]           
  3                  -1  1    461440  ultralytics.nn.modules.Conv                  [160, 320, 3, 2]              
  4                  -1  6   3281920  ultralytics.nn.modules.C2f                   [320, 320, 6, True]           
  5                  -1  1   1844480  ultralytics.nn.modules.Conv                  [320, 640, 3, 2]              
  6                  -1  6  13117440  ultralytics.nn.modules.C2f                   [640, 640, 6, True]           
  7                  -1  1   3687680  ultralytics.nn.modules.Conv                  [640, 640, 3, 2]              
  8                  -1  3   6969600  ultralytics.nn.modules.C2f                   [640, 640, 3, True]           
  9                  -1  1   1025920  ultralytics.nn.modules.SPPF                  [640, 640, 5]                 
 10                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 11             [-1, 6]  1         0  ultralytics.nn.modules.Concat                [1]                           
 12                  -1  3   7379200  ultralytics.nn.modules.C2f                   [1280, 640, 3]                
 13                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 14             [-1, 4]  1         0  ultralytics.nn.modules.Concat                [1]                           
 15                  -1  3   1948800  ultralytics.nn.modules.C2f                   [960, 320, 3]                 
 16                  -1  1    922240  ultralytics.nn.modules.Conv                  [320, 320, 3, 2]              
 17            [-1, 12]  1         0  ultralytics.nn.modules.Concat                [1]                           
 18                  -1  3   7174400  ultralytics.nn.modules.C2f                   [960, 640, 3]                 
 19                  -1  1   3687680  ultralytics.nn.modules.Conv                  [640, 640, 3, 2]              
 20             [-1, 9]  1         0  ultralytics.nn.modules.Concat                [1]                           
 21                  -1  3   7379200  ultralytics.nn.modules.C2f                   [1280, 640, 3]                
 22        [15, 18, 21]  1   8718931  ultralytics.nn.modules.Detect                [1, [320, 640, 640]]          
Model summary: 365 layers, 68153571 parameters, 68153555 gradients, 258.1 GFLOPs

Transferred 589/595 items from pretrained weights
WARNING  ClearML installed but not initialized correctly, not logging this run. It seems ClearML is not configured on this machine!
To get started with ClearML, setup your own 'clearml-server' or create a free account at https://app.clear.ml
Setup instructions can be found here: https://clear.ml/docs
WARNING  TensorBoard not initialized correctly, not logging this run. 'NoneType' object is not callable
optimizer: SGD(lr=0.01) with parameter groups 97 weight(decay=0.0), 104 weight(decay=0.00046875), 103 bias
train: Scanning C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\Imagenes_rios_lagos\labels\train... 208 images, 4 backgrounds, 0 corrupt: 100%|██████████| 210/210 [00:00<00:00, 1375.96it/s]
train: New cache created: C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\Imagenes_rios_lagos\labels\train.cache
val: Scanning C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\Imagenes_rios_lagos\labels\val.cache... 69 images, 1 backgrounds, 0 corrupt: 100%|██████████| 70/70 [00:00<?, ?it/s]
Plotting labels to runs\detect\models\yolov8x_water32\labels.jpg... 
Image sizes 640 train, 640 val
Using 0 dataloader workers
Logging results to runs\detect\models\yolov8x_water32
Starting training for 10 epochs...
Closing dataloader mosaic

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       1/10         0G      1.079      2.868      1.499         10        640: 100%|██████████| 21/21 [51:52<00:00, 148.23s/it] 
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [03:04<00:00, 46.11s/it]
                   all         70         69      0.908      0.913      0.928      0.799

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       2/10         0G     0.4596     0.9645      1.088         10        640: 100%|██████████| 21/21 [48:30<00:00, 138.60s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [03:03<00:00, 45.84s/it]
                   all         70         69      0.867      0.899      0.899      0.747

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       3/10         0G     0.4469     0.7604      1.062         10        640: 100%|██████████| 21/21 [44:35<00:00, 127.42s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [02:53<00:00, 43.45s/it]
                   all         70         69      0.808      0.841      0.842      0.682

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       4/10         0G     0.4757     0.6395      1.036         10        640: 100%|██████████| 21/21 [59:38<00:00, 170.41s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:52<00:00, 73.17s/it]
                   all         70         69      0.876      0.942      0.944      0.822

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       5/10         0G     0.5033      0.935      1.064         10        640: 100%|██████████| 21/21 [1:11:22<00:00, 203.94s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:47<00:00, 71.95s/it]
                   all         70         69      0.843      0.942       0.96      0.844

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       6/10         0G     0.6056     0.6176      1.111         10        640: 100%|██████████| 21/21 [1:11:37<00:00, 204.66s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:51<00:00, 72.79s/it]
                   all         70         69      0.793      0.779      0.827      0.647

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       7/10         0G     0.4995     0.4811      1.073         10        640: 100%|██████████| 21/21 [1:08:55<00:00, 196.94s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:38<00:00, 69.70s/it]
                   all         70         69      0.832      0.855      0.883      0.758

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       8/10         0G      0.517      0.478      1.126         10        640: 100%|██████████| 21/21 [1:11:11<00:00, 203.41s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:54<00:00, 73.71s/it]
                   all         70         69      0.871      0.982      0.965      0.842

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       9/10         0G      0.475     0.4025      1.045         10        640: 100%|██████████| 21/21 [1:09:51<00:00, 199.59s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:42<00:00, 70.63s/it]
                   all         70         69      0.925      0.913      0.957      0.827

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      10/10         0G     0.4437     0.4091      1.046         10        640: 100%|██████████| 21/21 [1:10:54<00:00, 202.59s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:50<00:00, 72.57s/it]
                   all         70         69      0.927      0.986       0.98      0.858

10 epochs completed in 11.203 hours.
Optimizer stripped from runs\detect\models\yolov8x_water32\weights\last.pt, 136.7MB
Optimizer stripped from runs\detect\models\yolov8x_water32\weights\best.pt, 136.7MB

Validating runs\detect\models\yolov8x_water32\weights\best.pt...
Ultralytics YOLOv8.0.53  Python-3.10.11 torch-1.13.1+cpu CPU
Model summary (fused): 268 layers, 68124531 parameters, 0 gradients, 257.4 GFLOPs
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [04:34<00:00, 68.61s/it]
                   all         70         69      0.927      0.986       0.98       0.86
Speed: 4.6ms preprocess, 3867.6ms inference, 0.0ms loss, 2.2ms postprocess per image
Results saved to runs\detect\models\yolov8x_water32
Ultralytics YOLOv8.0.53  Python-3.10.11 torch-1.13.1+cpu CPU
Model summary (fused): 268 layers, 68124531 parameters, 0 gradients, 257.4 GFLOPs
val: Scanning C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\Imagenes_rios_lagos\labels\val.cache... 69 images, 1 backgrounds, 0 corrupt: 100%|██████████| 70/70 [00:00<?, ?it/s]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 7/7 [04:44<00:00, 40.60s/it]
                   all         70         69      0.927      0.986       0.98      0.864
Speed: 4.7ms preprocess, 4003.2ms inference, 0.0ms loss, 1.9ms postprocess per image
Results saved to runs\detect\models\yolov8x_water33

Se entrena un nuevo modelo YOLOv8x para detectar “boundary boxes” que contengan agua de rios y lagos, pero sin sus contornos. Este nuevo modelo servirá para complementar lo detectado en el anterior e intentar reducir el numero de falsos positivos.

In [13]:
# Load a pretrained YOLO model (recommended for training)
model = YOLO(model=PATH_MODELO_YOLO_COCO)

# Train the model using the 'coco128.yaml' dataset for 3 epochs
results = model.train(data='./resources/YOLO_yaml/yolo_v8_rivers_lakes2.yaml', epochs=10, batch=10,name='./models/yolov8x_rivers_lakes2')

# Evaluate the model's performance on the validation set
results = model.val()
New https://pypi.org/project/ultralytics/8.0.96 available  Update with 'pip install -U ultralytics'
Ultralytics YOLOv8.0.53  Python-3.10.11 torch-1.13.1+cpu CPU
yolo\engine\trainer: task=detect, mode=train, model=./models/yolov8x.pt, data=./resources/YOLO_yaml/yolo_v8_rivers_lakes2.yaml, epochs=10, patience=50, batch=10, imgsz=640, save=True, save_period=-1, cache=False, device=None, workers=8, project=None, name=./models/yolov8x_rivers_lakes2, exist_ok=False, pretrained=False, optimizer=SGD, verbose=True, seed=0, deterministic=True, single_cls=False, image_weights=False, rect=False, cos_lr=False, close_mosaic=10, resume=False, overlap_mask=True, mask_ratio=4, dropout=0.0, val=True, split=val, save_json=False, save_hybrid=False, conf=None, iou=0.7, max_det=300, half=False, dnn=False, plots=True, source=None, show=False, save_txt=False, save_conf=False, save_crop=False, hide_labels=False, hide_conf=False, vid_stride=1, line_thickness=3, visualize=False, augment=False, agnostic_nms=False, classes=None, retina_masks=False, boxes=True, format=torchscript, keras=False, optimize=False, int8=False, dynamic=False, simplify=False, opset=None, workspace=4, nms=False, lr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=7.5, cls=0.5, dfl=1.5, fl_gamma=0.0, label_smoothing=0.0, nbs=64, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.0, copy_paste=0.0, cfg=None, v5loader=False, tracker=botsort.yaml, save_dir=runs\detect\models\yolov8x_rivers_lakes2
Overriding model.yaml nc=80 with nc=1

                   from  n    params  module                                       arguments                     
  0                  -1  1      2320  ultralytics.nn.modules.Conv                  [3, 80, 3, 2]                 
  1                  -1  1    115520  ultralytics.nn.modules.Conv                  [80, 160, 3, 2]               
  2                  -1  3    436800  ultralytics.nn.modules.C2f                   [160, 160, 3, True]           
  3                  -1  1    461440  ultralytics.nn.modules.Conv                  [160, 320, 3, 2]              
  4                  -1  6   3281920  ultralytics.nn.modules.C2f                   [320, 320, 6, True]           
  5                  -1  1   1844480  ultralytics.nn.modules.Conv                  [320, 640, 3, 2]              
  6                  -1  6  13117440  ultralytics.nn.modules.C2f                   [640, 640, 6, True]           
  7                  -1  1   3687680  ultralytics.nn.modules.Conv                  [640, 640, 3, 2]              
  8                  -1  3   6969600  ultralytics.nn.modules.C2f                   [640, 640, 3, True]           
  9                  -1  1   1025920  ultralytics.nn.modules.SPPF                  [640, 640, 5]                 
 10                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 11             [-1, 6]  1         0  ultralytics.nn.modules.Concat                [1]                           
 12                  -1  3   7379200  ultralytics.nn.modules.C2f                   [1280, 640, 3]                
 13                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 14             [-1, 4]  1         0  ultralytics.nn.modules.Concat                [1]                           
 15                  -1  3   1948800  ultralytics.nn.modules.C2f                   [960, 320, 3]                 
 16                  -1  1    922240  ultralytics.nn.modules.Conv                  [320, 320, 3, 2]              
 17            [-1, 12]  1         0  ultralytics.nn.modules.Concat                [1]                           
 18                  -1  3   7174400  ultralytics.nn.modules.C2f                   [960, 640, 3]                 
 19                  -1  1   3687680  ultralytics.nn.modules.Conv                  [640, 640, 3, 2]              
 20             [-1, 9]  1         0  ultralytics.nn.modules.Concat                [1]                           
 21                  -1  3   7379200  ultralytics.nn.modules.C2f                   [1280, 640, 3]                
 22        [15, 18, 21]  1   8718931  ultralytics.nn.modules.Detect                [1, [320, 640, 640]]          
Model summary: 365 layers, 68153571 parameters, 68153555 gradients, 258.1 GFLOPs

Transferred 589/595 items from pretrained weights
WARNING  ClearML installed but not initialized correctly, not logging this run. It seems ClearML is not configured on this machine!
To get started with ClearML, setup your own 'clearml-server' or create a free account at https://app.clear.ml
Setup instructions can be found here: https://clear.ml/docs
WARNING  TensorBoard not initialized correctly, not logging this run. 'NoneType' object is not callable
optimizer: SGD(lr=0.01) with parameter groups 97 weight(decay=0.0), 104 weight(decay=0.00046875), 103 bias
train: Scanning C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\imagenes_rios_lagos2\labels\train... 100 images, 10 backgrounds, 0 corrupt: 100%|██████████| 110/110 [00:00<00:00, 595.70it/s]
train: New cache created: C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\imagenes_rios_lagos2\labels\train.cache
val: Scanning C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\imagenes_rios_lagos2\labels\val... 21 images, 10 backgrounds, 0 corrupt: 100%|██████████| 31/31 [00:00<00:00, 788.07it/s]
val: New cache created: C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\imagenes_rios_lagos2\labels\val.cache
Plotting labels to runs\detect\models\yolov8x_rivers_lakes2\labels.jpg... 
Image sizes 640 train, 640 val
Using 0 dataloader workers
Logging results to runs\detect\models\yolov8x_rivers_lakes2
Starting training for 10 epochs...
Closing dataloader mosaic

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       1/10         0G      1.218      4.048      1.567          8        640: 100%|██████████| 11/11 [22:02<00:00, 120.25s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [01:33<00:00, 46.66s/it]
                   all         31         22      0.456      0.419      0.295       0.23

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       2/10         0G     0.8994      2.122      1.323         10        640: 100%|██████████| 11/11 [22:34<00:00, 123.14s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [01:27<00:00, 43.79s/it]
                   all         31         22      0.513      0.773      0.575      0.449

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       3/10         0G     0.7342      1.459      1.195          8        640: 100%|██████████| 11/11 [23:51<00:00, 130.12s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [01:32<00:00, 46.46s/it]
                   all         31         22      0.681      0.682       0.56      0.446

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       4/10         0G     0.6737      1.497      1.154         10        640: 100%|██████████| 11/11 [22:29<00:00, 122.67s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [01:35<00:00, 47.78s/it]
                   all         31         22      0.521      0.642      0.551      0.458

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       5/10         0G     0.5749      1.011       1.09         10        640: 100%|██████████| 11/11 [22:34<00:00, 123.15s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [01:32<00:00, 46.30s/it]
                   all         31         22      0.599      0.636       0.54      0.465

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       6/10         0G     0.5638     0.9851      1.099          8        640: 100%|██████████| 11/11 [33:00<00:00, 180.04s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [02:25<00:00, 72.69s/it] 
                   all         31         22        0.6      0.681      0.508      0.451

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       7/10         0G      0.584       1.11      1.111          7        640: 100%|██████████| 11/11 [40:38<00:00, 221.72s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [02:26<00:00, 73.38s/it] 
                   all         31         22      0.693      0.455      0.553      0.453

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       8/10         0G      0.604      1.091      1.122          9        640: 100%|██████████| 11/11 [40:35<00:00, 221.42s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [02:26<00:00, 73.07s/it] 
                   all         31         22      0.493      0.574      0.502       0.42

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
       9/10         0G     0.5466       0.91       1.07          8        640: 100%|██████████| 11/11 [40:04<00:00, 218.60s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [02:22<00:00, 71.30s/it] 
                   all         31         22       0.47      0.591      0.417      0.362

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      10/10         0G     0.4777     0.7772      1.036         10        640: 100%|██████████| 11/11 [41:15<00:00, 225.07s/it]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [02:25<00:00, 72.61s/it] 
                   all         31         22      0.491      0.682      0.522      0.445

10 epochs completed in 5.495 hours.
Optimizer stripped from runs\detect\models\yolov8x_rivers_lakes2\weights\last.pt, 136.7MB
Optimizer stripped from runs\detect\models\yolov8x_rivers_lakes2\weights\best.pt, 136.7MB

Validating runs\detect\models\yolov8x_rivers_lakes2\weights\best.pt...
Ultralytics YOLOv8.0.53  Python-3.10.11 torch-1.13.1+cpu CPU
Model summary (fused): 268 layers, 68124531 parameters, 0 gradients, 257.4 GFLOPs
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 2/2 [02:28<00:00, 74.37s/it] 
                   all         31         22      0.599      0.636       0.54      0.465
Speed: 6.2ms preprocess, 4769.5ms inference, 0.0ms loss, 4.2ms postprocess per image
Results saved to runs\detect\models\yolov8x_rivers_lakes2
Ultralytics YOLOv8.0.53  Python-3.10.11 torch-1.13.1+cpu CPU
Model summary (fused): 268 layers, 68124531 parameters, 0 gradients, 257.4 GFLOPs
val: Scanning C:\Users\dcsj\Repositorios\UOC_Master_DS_TFM\image_classifier\resources\images\YOLO_format\imagenes_rios_lagos2\labels\val.cache... 21 images, 10 backgrounds, 0 corrupt: 100%|██████████| 31/31 [00:00<?, ?it/s]
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100%|██████████| 4/4 [02:16<00:00, 34.18s/it]
                   all         31         22      0.599      0.636      0.539      0.465
Speed: 6.0ms preprocess, 4377.2ms inference, 0.0ms loss, 4.5ms postprocess per image
Results saved to runs\detect\models\yolov8x_rivers_lakes22

5. Detección de "rios y lagos" en imágenes¶

Se pasa a detectar las imágenes de agua con el primer modelo, en que se ha entrenado para detectar todo el contorno de rios y lagos.

In [ ]:
objectdetector = ObjectDetectionHelper()
objectdetector.detectionTrainedClasses('./models/yolov8x_rivers_lakes.pt',PATH_IMAGENES_A_ANALIZAR,0.5)

Se pasa a realizar la detección de agua de rios y lagos, pero con el modelo que detecta agua sin elementos intermedios, ni contornos.

In [ ]:
objectdetector = ObjectDetectionHelper()
objectdetector.detectionTrainedClasses('./models/yolov8x_rivers_lakes2.pt',PATH_IMAGENES_A_ANALIZAR,0.5)

6. Análisis de resultados de detección de "rios y lagos"¶

6.1. Carga de etiquetas manuales y grafo¶

Se carga el excel de etiquetas manuales en un dataframe que permitirá hacer comparativas con las imágenes en las que se ha detectado agua.

In [13]:
df_images_labeled_manually = pd.read_excel('./resources/manual_labels/etiquetado_manual_imagenes.xlsx',skiprows=1)
df_images_labeled_manually.head(10)
Out[13]:
Image Viaje Comida Cena Cumpleaños Retrato Maria Biel Yo Piscina ... Tenedor Cuchillo Cuchara bowl pastel silla Mesa Plato Vaso Llama vela
0 20210724_100218.jpg 0 0 0 0 0 0 1 0 0 ... 0 0 0 0 0 1 1 1 0 0
1 20210724_104730.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
2 20210724_104736.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
3 20210724_104739.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
4 20210724_104743.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
5 20210724_104757.jpg 0 0 0 0 0 1 1 0 1 ... 0 0 0 0 0 0 0 0 0 0
6 20210724_200143.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 1 0 0 0
7 20210724_200145.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 1 0 0 0
8 20210724_200231_02.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 1 0 0 0
9 20210724_200231_03.jpg 0 0 0 0 1 1 0 1 0 ... 0 0 0 0 0 1 0 0 0 0

10 rows × 27 columns

Se filtran el dataframe para obtener aquellas imagenes que contienen lagos o rios en la escena.

In [14]:
df_images_labeled_manually_rios_lagos = df_images_labeled_manually[df_images_labeled_manually['Rios/lagos'] == 1].copy()
df_images_labeled_manually_rios_lagos
Out[14]:
Image Viaje Comida Cena Cumpleaños Retrato Maria Biel Yo Piscina ... Tenedor Cuchillo Cuchara bowl pastel silla Mesa Plato Vaso Llama vela
38 20210725_174439.jpg 0 0 0 0 0 1 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
39 20210725_174449.jpg 0 0 0 0 0 1 1 0 0 ... 0 0 0 0 0 0 0 0 0 0
40 20210725_174454.jpg 0 0 0 0 0 1 1 0 0 ... 0 0 0 0 0 0 0 0 0 0
41 20210725_174458.jpg 0 0 0 0 0 1 1 0 0 ... 0 0 0 0 0 0 0 0 0 0
42 20210725_174500.jpg 0 0 0 0 0 1 1 0 0 ... 0 0 0 0 0 0 0 0 0 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1164 20220825_132316.jpg 1 0 0 0 0 0 1 0 0 ... 0 0 0 0 0 0 0 0 0 0
1330 20230106_175958.jpg 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1331 20230106_180004.jpg 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1332 20230106_180045.jpg 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1333 20230106_180048.jpg 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0

355 rows × 27 columns

Se cargan las imágenes con agua detectada tanto del primer modelo con boundary boxes completos, como del segundo modelo con los boundary boxes marcando solo agua

In [15]:
# Se crea el grafo
imagesGraphDB = ImagesGraphDB()
# Se cargan las imágenes con agua detectada a partir del primer modelo con boundary boxes completos
imagesGraphDB.load_graph_from_yolo_detected_objects_txt_files(PATH_RIVERS_LAKES_LABELS_REF_FILE,PATH_RIVERS_LAKES_DETECTED_LABELS)
imagesGraphDB.load_graph_from_yolo_detected_objects_txt_files(PATH_RIVERS_LAKES2_LABELS_REF_FILE,PATH_RIVERS_LAKES2_DETECTED_LABELS)

imagesGraphDB.write_gml_file(PATH_RIVERS_LAKES_GML_FILE)

6.2. Evaluación de modelos¶

6.2.1. Imagenes modelo 1 (Ríos y lagos con contorno)¶

Ahora se cargan las imágenes en las que se ha detectado agua de rios y lagos mediante el modelo entrenado con el primer tipo de "boundary boxes".

In [16]:
# Se obtiene las imágenes del grafo que se relacionan con los nodos "person" y "cake"
object_classes_to_filter = ['water'] # Se cogerán las imágenes con la etiqueta "water"
neighbors_rivers_lakes = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(object_classes_to_filter,[0.99]) # Se cargan las imagenes, pero solo aquellas con una mayor confianza

Se muestra una imágen obtenida bajo estos criterios.

In [17]:
rivers_lakes_images_filepath_list = []
rivers_lakes_images_filename_list = []
for neighbor_river_lake in neighbors_rivers_lakes:
    rivers_lakes_images_filepath_list.append(os.path.join(PATH_IMAGENES_A_ANALIZAR+'_resized',neighbor_river_lake))
    rivers_lakes_images_filename_list.append(neighbor_river_lake)

# Se cargan las imágenes en formato PIL en una lista pora poderlas mostrar en un grid
images_rgb_list= []
for i, image in enumerate(rivers_lakes_images_filepath_list):
    image_rgb = Image.open(image)
    images_rgb_list.append(image_rgb)

# Se muestran las imágenes en un grid usando un metodo propio del helper de imágenes
%matplotlib inline
ImageHelper.display_images(images_rgb_list,columns=3,max_images=1,label_font_size=12)
Showing 1 images of 7:

6.2.3. Matriz de confusión modelo 1 (Ríos y lagos con contorno)¶

Se obtiene la matriz de confusión de las predicciones con el primer modelo de detección de agua de rios/lagos.

In [18]:
# Primero se obtienen los valores de "Rios/lagos" etiquetados manualmente en todas las imágenes.
df_images_rivers_lakes_manual = df_images_labeled_manually[['Image','Rios/lagos']].copy()
# Se obtienen las imágenes del grafo con una confidencialidad mayor de 0.8
neighbors_rivers_lakes = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(['water'],[0.7])
# Se obtiene el dataframe de imágenes detectadas mediante el primer modelo
df_images_water_detected = pd.DataFrame(neighbors_rivers_lakes, columns=['Image'])
df_images_water_detected['Rios/lagos predicted'] =1
# Se cruzan ambos dataframes
df_images_water_analysis = df_images_rivers_lakes_manual.merge(df_images_water_detected , how="left", left_on="Image", right_on="Image")
# Se informa con valor 0 aquellas imagenes sin detección de rios y lagos
# en lugar de NA
df_images_water_analysis['Rios/lagos predicted'] = df_images_water_analysis['Rios/lagos predicted']. fillna(0)

# Se convierte a int la columna ya que se usa como booleano
df_images_water_analysis['Rios/lagos predicted'] = df_images_water_analysis['Rios/lagos predicted'].astype('int')

df_images_water_analysis
Out[18]:
Image Rios/lagos Rios/lagos predicted
0 20210724_100218.jpg 0 0
1 20210724_104730.jpg 0 1
2 20210724_104736.jpg 0 1
3 20210724_104739.jpg 0 1
4 20210724_104743.jpg 0 1
... ... ... ...
1360 IMG_20180505_212946_BURST001_COVER.jpg 0 1
1361 IMG_20180505_212946_BURST002.jpg 0 1
1362 IMG_20180505_212946_BURST003.jpg 0 1
1363 IMG_20180822_173932.jpg 0 0
1364 IMG_20180824_195613.jpg 0 0

1365 rows × 3 columns

In [19]:
from sklearn import metrics

# Se cargan los resultados de la predicción y el real
images_rivers_lakes_actual= df_images_water_analysis['Rios/lagos'].to_numpy()
images_rivers_lakes_predicted= df_images_water_analysis['Rios/lagos predicted'].to_numpy()
# Se cargan los datos para generar la matriz de confusión
confusion_matrix = metrics.confusion_matrix(images_rivers_lakes_actual, images_rivers_lakes_predicted)
cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = confusion_matrix, display_labels = [False, True])
# Se muestra el gráfico
cm_display.plot()
plt.show()

6.2.5. Métricas modelo 1 (Ríos y lagos con contorno)¶

In [20]:
analyticsHelper = AnalyticsHelper()
metrics = analyticsHelper.getBinaryClassificationConfusionMatrixMetrics(confusion_matrix)
metrics
Out[20]:
{'recall': 0.6535211267605634,
 'precision': 0.3772357723577236,
 'accuracy': 0.6293040293040293,
 'specificity': 0.6207920792079208,
 'f1_score': 0.47835051546391755}

6.2.2. Imágenes modelo 2 (Rios y lagos sin elementos intermedios)¶

Se cargan las imágenes en que se han detectado agua utilizando el modelo entrenado con "boundary boxes" sin elementos intermedios y recortando imágenes para que no apareciesen otros elementos que no fuesen el agua.

In [21]:
# Se cargan las imágenes con agua detectada a partir del primer modelo con boundary boxes completos
imagesGraphDB.load_graph_from_yolo_detected_objects_txt_files(PATH_RIVERS_LAKES2_LABELS_REF_FILE,PATH_RIVERS_LAKES2_DETECTED_LABELS)

# Se obtiene las imágenes del grafo que se relacionan con los nodos "person" y "cake"
object_classes_to_filter = ['water2'] # Se cogerán las imagenes con pasteles, personas y velas encendidas
neighbors_rivers_lakes2 = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(object_classes_to_filter,[0.95])

Se muestra una imágen obtenida bajo estos criterios.

In [22]:
rivers_lakes2_images_filepath_list = []
rivers_lakes2_images_filename_list = []
for neighbor_river_lake in neighbors_rivers_lakes2:
    rivers_lakes2_images_filepath_list.append(os.path.join(PATH_IMAGENES_A_ANALIZAR+'_resized',neighbor_river_lake))
    rivers_lakes2_images_filename_list.append(neighbor_river_lake)

# Se cargan las imágenes en formato PIL en una lista pora poderlas mostrar en un grid
images_rgb_list= []
for i, image in enumerate(rivers_lakes2_images_filepath_list):
    image_rgb = Image.open(image)
    images_rgb_list.append(image_rgb)

# Se muestran las imagenes en un grid usando un metodo propio del helper de imágenes
%matplotlib inline
ImageHelper.display_images(images_rgb_list,columns=3,max_images=1,label_font_size=12)
Showing 1 images of 4:

6.2.4. Matriz de confusión modelo 2 (Ríos y lagos sin elementos intermedios)¶

Se repite el ejercicio de obtención de la matriz de confusión, pero esta vez para las predicciones del segundo modelo de detección de agua de rios y lagos.

In [23]:
neighbors_rivers_lakes2 = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(['water2'],[0.7])
# Se obtiene el dataframe de imágenes detectadas mediante el segundo modelo
df_images_water2_detected = pd.DataFrame(neighbors_rivers_lakes2, columns=['Image'])
df_images_water2_detected['Rios/lagos predicted'] =1
# Se cruzan ambos dataframes
df_images_water2_analysis = df_images_rivers_lakes_manual.merge(df_images_water2_detected , how="left", left_on="Image", right_on="Image")
# Se informa con valor 0 aquellas imagenes sin detección de rios y lagos
# en lugar de NA
df_images_water2_analysis['Rios/lagos predicted'] = df_images_water2_analysis['Rios/lagos predicted']. fillna(0)

# Se convierte a int la columna ya que se usa como booleano
df_images_water2_analysis['Rios/lagos predicted'] = df_images_water2_analysis['Rios/lagos predicted'].astype('int')

df_images_water2_analysis
Out[23]:
Image Rios/lagos Rios/lagos predicted
0 20210724_100218.jpg 0 0
1 20210724_104730.jpg 0 0
2 20210724_104736.jpg 0 0
3 20210724_104739.jpg 0 0
4 20210724_104743.jpg 0 0
... ... ... ...
1360 IMG_20180505_212946_BURST001_COVER.jpg 0 1
1361 IMG_20180505_212946_BURST002.jpg 0 1
1362 IMG_20180505_212946_BURST003.jpg 0 1
1363 IMG_20180822_173932.jpg 0 0
1364 IMG_20180824_195613.jpg 0 0

1365 rows × 3 columns

In [28]:
from sklearn import metrics
images_rivers_lakes2_actual= df_images_water2_analysis['Rios/lagos'].to_numpy()
images_rivers_lakes2_predicted= df_images_water2_analysis['Rios/lagos predicted'].to_numpy()

confusion_matrix2 = metrics.confusion_matrix(images_rivers_lakes2_actual, images_rivers_lakes2_predicted)

cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = confusion_matrix2, display_labels = [False, True])

cm_display.plot()
plt.show()

6.2.6. Métricas modelo 2 (Ríos y lagos sin elementos intermedios)¶

In [29]:
analyticsHelper = AnalyticsHelper()
metrics2 = analyticsHelper.getBinaryClassificationConfusionMatrixMetrics(confusion_matrix2)
metrics2
Out[29]:
{'recall': 0.1267605633802817,
 'precision': 0.45918367346938777,
 'accuracy': 0.734065934065934,
 'specificity': 0.9475247524752475,
 'f1_score': 0.1986754966887417}

6.2.7. Imágenes combinación de modelos¶

In [30]:
# Se cargan las imágenes que han sido detectadas en ambos modelos
neighbors_rivers_lakes3 = imagesGraphDB.get_images_containing_list_object_types_with_min_confidence(['water','water2'],[0.7,0.7])
# Se obtiene el dataframe de imágenes detectadas mediante el segundo modelo
df_images_water3_detected = pd.DataFrame(neighbors_rivers_lakes3, columns=['Image'])
df_images_water3_detected['Rios/lagos predicted'] =1
# Se cruzan ambos dataframes
df_images_water3_analysis = df_images_rivers_lakes_manual.merge(df_images_water3_detected , how="left", left_on="Image", right_on="Image")
# Se informa con valor 0 aquellas imagenes sin detección de rios y lagos
# en lugar de NA
df_images_water3_analysis['Rios/lagos predicted'] = df_images_water3_analysis['Rios/lagos predicted']. fillna(0)

# Se convierte a int la columna ya que se usa como booleano
df_images_water3_analysis['Rios/lagos predicted'] = df_images_water3_analysis['Rios/lagos predicted'].astype('int')

df_images_water3_analysis
Out[30]:
Image Rios/lagos Rios/lagos predicted
0 20210724_100218.jpg 0 0
1 20210724_104730.jpg 0 0
2 20210724_104736.jpg 0 0
3 20210724_104739.jpg 0 0
4 20210724_104743.jpg 0 0
... ... ... ...
1360 IMG_20180505_212946_BURST001_COVER.jpg 0 1
1361 IMG_20180505_212946_BURST002.jpg 0 1
1362 IMG_20180505_212946_BURST003.jpg 0 1
1363 IMG_20180822_173932.jpg 0 0
1364 IMG_20180824_195613.jpg 0 0

1365 rows × 3 columns

6.2.8. Matriz de confusión de combinación de modelos¶

In [31]:
from sklearn import metrics

images_rivers_lakes3_actual= df_images_water3_analysis['Rios/lagos'].to_numpy()
images_rivers_lakes3_predicted= df_images_water3_analysis['Rios/lagos predicted'].to_numpy()

confusion_matrix3 = metrics.confusion_matrix(images_rivers_lakes3_actual, images_rivers_lakes3_predicted)

cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = confusion_matrix3, display_labels = [False, True])

cm_display.plot()
plt.show()

6.2.9. Métricas de combinación de modelos¶

In [32]:
analyticsHelper = AnalyticsHelper()
metrics3 = analyticsHelper.getBinaryClassificationConfusionMatrixMetrics(confusion_matrix3)
metrics3
Out[32]:
{'recall': 0.12394366197183099,
 'precision': 0.6111111111111112,
 'accuracy': 0.7516483516483516,
 'specificity': 0.9722772277227723,
 'f1_score': 0.20608899297423885}
In [33]:
# Se guarda el grafo del modelo combinado al tener mejores resultados que los resultados
# de modelos por separado
# Se guarda el grafo de las imagenes de rios y lagos
imagesGraphDB.write_gml_file(PATH_RIVERS_LAKES_GML_FILE)

6.2.10. Conclusiones¶

Se observa una mejoría en la precisión disminuyendo significativamente el número de falsos positivos. La combinación de los resultados, buscando la intersección de resultados provoca una mejora de las métricas con respecto de las que se obtienen en los modelos por separado.